{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 函数\n",
    "函数是组织好的，可重复使用的，用来实现单一，或相关联功能的代码段。\n",
    "\n",
    "函数能提高应用的模块性，和代码的重复利用率。你已经知道Python提供了许多内建函数，比如print()。但你也可以自己创建函数，这被叫做用户自定义函数。\n",
    "\n",
    "#### 定义一个函数\n",
    "你可以定义一个由自己想要功能的函数，以下是简单的规则：\n",
    "\n",
    "函数代码块以 def 关键词开头，后接函数标识符名称和圆括号 ()。\n",
    "任何传入参数和自变量必须放在圆括号中间，圆括号之间可以用于定义参数。\n",
    "函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。\n",
    "函数内容以冒号起始，并且缩进。\n",
    "return [表达式] 结束函数，选择性地返回一个值给调用方。不带表达式的return相当于返回 None。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 实例\n",
    "def hello():\n",
    "    print(\"Hello World!\")\n",
    "    # return 不是必须的，如果不需要返回数据，可以不用return\n",
    "\n",
    "def my_abs(x):\n",
    "    if x >= 0:\n",
    "        return x  # 返回给调用函数的一方\n",
    "    else:\n",
    "        return -x  # 返回给调用函数的一方   思考：这是在求什么？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "hello()\n",
    "hello()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 调用函数   \n",
    "result = my_abs(-9)  # 求数字的绝对值\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习1\n",
    "\n",
    "写一个函数，打印五次【我是最厉害的！】"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def print5time():\n",
    "    ???   \n",
    "       ???"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print5time()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习2\n",
    "\n",
    "写一个函数，打印五次你的座右铭"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def print5time_2(words):# 接受words变量\n",
    "    ???\n",
    "       ???\n",
    "      "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "my_words = input(\"输入你的座右铭：\") \n",
    "print(\"=========结果如下========\")\n",
    "print5time_2(my_words)  #传参（）内容变量自定义"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 参数传递\n",
    "\n",
    "可更改(mutable)与不可更改(immutable)对象\n",
    "在 python 中，strings, tuples, 和 numbers 是不可更改的对象，而 list,dict 等则是可以修改的对象。\n",
    "\n",
    "不可变类型：变量赋值 a=5 后再赋值 a=10，这里实际是新生成一个 int 值对象 10，再让 a 指向它，而 5 被丢弃，不是改变a的值，相当于新生成了a。\n",
    "\n",
    "可变类型：变量赋值 la=[1,2,3,4] 后再赋值 la[2]=5 则是将 list la 的第三个元素值更改，本身la没有动，只是其内部的一部分值被修改了。\n",
    "\n",
    "python 函数的参数传递：\n",
    "\n",
    "不可变类型：如 整数、字符串、元组。如fun（a），传递的只是a的值，没有影响a对象本身。比如在 fun（a）内部修改 a 的值，只是修改另一个复制的对象，不会影响 a 本身。\n",
    "\n",
    "可变类型：如 列表，字典。如 fun（la），则是将 la 真正的传过去，修改后fun外部的la也会受影响\n",
    "\n",
    "python 中一切都是对象，严格意义我们不能说值传递还是引用传递，我们应该说传不可变对象和传可变对象"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 传不可变对象实例\n",
    "def ChangeInt(a):\n",
    "    print('函数内取值', a)\n",
    "    a = 10\n",
    "    print('Has changed', a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = 2\n",
    "ChangeInt(a)\n",
    "print('函数外取值', a) # 结果是 2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 传可变对象实例\n",
    "def changeme(mylist):\n",
    "    # \"修改传入的列表\"\n",
    "    print('函数内取值是', mylist)\n",
    "    mylist.append(40)\n",
    "    print(\"函数操作之后的内取值是: \", mylist)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 调用changeme函数\n",
    "mylist = [10,20,30]\n",
    "changeme(mylist)\n",
    "print(\"函数外取值是: \", mylist)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 传可变对象实例\n",
    "def changeme(mylist):\n",
    "   # \"修改传入的列表\"\n",
    "   mylist.append([1,2,3,4])\n",
    "   print(\"函数内取值: \", mylist)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# 调用changeme函数\n",
    "mylist = [10,20,30]\n",
    "changeme(mylist)\n",
    "print(\"函数外取值: \", mylist)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "观察变量的内存地址，判断是不可变变量还是可变变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 不可变变量\n",
    "a = 10\n",
    "print('a的内存ID:',id(a))  \n",
    "b = a\n",
    "print('b=a,b的内存ID:',id(b)) # a和b值一样，地址一样\n",
    "print(\"a和b虽然名字不一样，但他们都是一样的内存地址，所以在计算机看来是一样的。\")\n",
    "a = a + 10\n",
    "print('a=a+10,a的内存ID:',id(a)) # a虽然名字不变，但a已经变了了20，10不等于20，内存地址发生改变了\n",
    "print(\"a虽然名字不变，但内存地址发生改变了，所以a（整数）是不可变变量\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 可变变量\n",
    "l = [1,2,3]\n",
    "print(\"l列表的内存地址：\",id(l))\n",
    "l.append(4)\n",
    "print(\"l列表发生变化后的内存地址：\",id(l)) # l的值改变了，但地址保持一样"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 参数\n",
    "以下是调用函数时可使用的正式参数类型：\n",
    "\n",
    "1. 必备参数  -- 以上都是必备参数\n",
    "2. 关键字参数\n",
    "3. 默认参数\n",
    "4. 不定长参数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 关键字参数\n",
    "关键字参数和函数调用关系紧密，函数调用使用关键字参数来确定传入的参数值。\n",
    "\n",
    "使用关键字参数允许函数调用时参数的顺序与声明时不一致，因为 Python 解释器能够用参数名匹配参数值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#函数说明\n",
    "def printinfo( name, age ):\n",
    "    # \"打印任何传入的字符串\"\n",
    "    print(\"Name: \", name)\n",
    "    print(\"Age \", age)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 必备参数,一定要按顺序name,age来填写，不能乱\n",
    "printinfo(\"Tom\", 44)\n",
    "# 调用printinfo函数--关键字参数，就可以不按顺序。\n",
    "printinfo(age=50, name=\"miki\")  #name是变量，miki是字符串"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 缺省参数\n",
    "调用函数时，缺省参数的值如果没有传入，则被认为是默认值。下例会打印默认的age，如果age没有被传入："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#可写函数说明\n",
    "def printinfo2( name, age = 35 ):\n",
    "    # \"打印任何传入的字符串\"\n",
    "    print(\"Name: \", name)\n",
    "    print(\"Age \", age)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 缺省参数\n",
    "printinfo2(\"Mery\") # age默认值就是35 \n",
    "printinfo2(\"Mery\",30) # age默认值就是35 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "printinfo2(\"Mery\",30,30)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 不定长参数\n",
    "你可能需要一个函数能处理比当初声明时更多的参数。这些参数叫做不定长参数，和上述2种参数不同，声明时不会命名。基本语法如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 函数说明\n",
    "def printinfo3(arg1, *vartuple ):  #\"*\"后面内容自定义\n",
    "    print(\"arg1:\", arg1)\n",
    "    for var in vartuple:\n",
    "        print(\"不定长参数\", var)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 调用printinfo 函数\n",
    "# printinfo3(10)\n",
    "print('-------------------')\n",
    "printinfo3(70, 60, 50)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "printinfo3(70, 60, 50,3,4,5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### return 语句\n",
    "return语句[表达式]退出函数，选择性地向调用方返回一个表达式。不带参数值的return语句返回None。之前的例子都没有示范如何返回数值，下例便告诉你怎么做："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 函数说明\n",
    "def my_sum( arg1, arg2 ):\n",
    "    # 返回2个参数的和.\"\n",
    "    total = arg1 + arg2\n",
    "    print(\"函数内求得值 : \", total)\n",
    "    return total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# my_sum函数\n",
    "total = my_sum(10,20)\n",
    "print(total)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def my_sum( arg1, arg2 ):\n",
    "    total = arg1 + arg2\n",
    "    \n",
    "    print(\"函数内求得值 : \", total) #因为函数变量total作用域是内部，需要通过return返回到函数外面\n",
    "    return total\n",
    "    # 如果没有return，total值在外面是拿不到"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "total = my_sum(5,6)\n",
    "print(total)  # None 就是没有东西返回"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 练习3 \n",
    "\n",
    "冰雹猜想验证，所有正整数如果是偶数就除以2，如果是奇数就乘以3倍加1，最后所有整数都会回到1。\n",
    "\n",
    "如果n是偶数：\n",
    "\n",
    "  n = n / 2\n",
    "  \n",
    "如果n是奇数：\n",
    "\n",
    "  n = n*3 +1"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def collatz_sequence(n):\n",
    "    \"\"\"\n",
    "    冰雹猜想：记录数值变化的过程（函数说明）\n",
    "    \n",
    "    \"\"\"  \n",
    "    sequence = [n]\n",
    "    while n ?? 1:   # 猜想结果是，所有正整数都会回到1\n",
    "        \n",
    "        if n ？ 2 == 0:  # 如果是偶数 N/2,求余数\n",
    "            ？？？\n",
    "        else: # 如果是奇数 3N+1\n",
    "            ？？？\n",
    "        \n",
    "        sequence.append(n)\n",
    "    ？？？"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "your_number = int(input(\"输入一个正整数\"))\n",
    "result = collatz_sequence(your_number)\n",
    "print(\"{}的变化是{}\".format(your_number, result))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "8//2 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "8/2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 变量作用域\n",
    "Python 中，程序的变量并不是在哪个位置都可以访问的，访问权限决定于这个变量是在哪里赋值的。\n",
    "\n",
    "变量的作用域决定了在哪一部分程序可以访问哪个特定的变量名称。Python的作用域一共有4种，分别是：\n",
    "\n",
    "1. L （Local） 局部作用域\n",
    "2. E （Enclosing） 闭包函数外的函数中\n",
    "3. G （Global） 全局作用域\n",
    "4. B （Built-in） 内建作用域\n",
    "\n",
    "以 L –> E –> G –>B 的规则查找，即：在局部找不到，便会去局部外的局部找（例如闭包），再找不到就会去全局找，再者去内建中找。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### L(Local)局部变量\n",
    "\n",
    "包含在def关键字定义的语句块中，即在函数中定义的变量。每当函数被调用时都会创建一个新的局部作用域。Python中也有递归，即自己调用自己，每次调用都会创建一个新的局部命名空间。在函数内部的变量声明，除非特别的声明为全局变量，否则均默认为局部变量。有些情况需要在函数内部定义全局变量，这时可以使用global关键字来声明变量的作用域为全局。局部变量域就像一个 栈，仅仅是暂时的存在，依赖创建该局部作用域的函数是否处于活动的状态。所以，一般建议尽量少定义全局变量，因为全局变量在模块文件运行的过程中会一直存在，占用内存空间。\n",
    "\n",
    "#### E(enclosing)闭包函数外的函数中\n",
    "\n",
    "E也包含在def关键字中，E和L是相对的，E相对于更上层的函数而言也是L。与L的区别在于，对一个函数而言，L是定义在此函数内部的局部作用域，而E是定义在此函数的上一层父级函数的局部作用域。主要是为了实现Python的闭包，而增加的实现。\n",
    "\n",
    "#### G(global)全局作用域\n",
    "\n",
    "即在模块层次中定义的变量，每一个模块都是一个全局作用域。也就是说，在模块文件顶层声明的变量具有全局作用域，从外部开来，模块的全局变量就是一个模块对象的属性。\n",
    "注意：全局作用域的作用范围仅限于单个模块文件内\n",
    "\n",
    "#### B(built-in)内建作用域\n",
    "系统内固定模块里定义的变量，如预定义在builtin 模块内的变量。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 2.9\n",
    "int(n)  # 内建作用域"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "count = 0  # 全局作用域\n",
    "\n",
    "def outer():\n",
    "    count = 1  # 闭包函数外的函数中\n",
    "    print('outer 函数里面', count)\n",
    "    \n",
    "    def inner():\n",
    "        count = 2  # 局部作用域\n",
    "        print('inner 函数里面', count)\n",
    "    \n",
    "    inner()\n",
    "    print('运行inner函数之后', count)\n",
    "\n",
    "outer()\n",
    "print('全局', count)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python 中只有模块（module），类（class）以及函数（def、lambda）才会引入新的作用域，其它的代码块（如 if/elif/else/、try/except、for/while等）是不会引入新的作用域的，也就是说这些语句内定义的变量，外部也可以访问，如下代码：\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "if True:\n",
    "    a = 0\n",
    "\n",
    "# 虽然没有定义a，但还是能访问,if 不是函数，所以会影响外面\n",
    "\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "函数内的b 5\n"
     ]
    },
    {
     "ename": "NameError",
     "evalue": "name 'bbbb' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m<ipython-input-3-798fa5959d23>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[0;32m      4\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[0mfun_b\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 6\u001b[1;33m \u001b[0mprint\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mbbbb\u001b[0m\u001b[1;33m)\u001b[0m  \u001b[1;31m# name 'bbbb' is not defined  外面没有定义b，所以b就没有定义\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mNameError\u001b[0m: name 'bbbb' is not defined"
     ]
    }
   ],
   "source": [
    "def fun_b():\n",
    "    bbbb = 5\n",
    "    print(\"函数内的b\",bbbb)\n",
    "\n",
    "fun_b()\n",
    "print(bbbb)  # name 'bbbb' is not defined  外面没有定义b，所以b就没有定义"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### global \n",
    "当内部作用域想修改外部作用域的变量时，就要用到global关键字了。\n",
    "\n",
    "以下实例修改全局变量 num："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "num = 1\n",
    "def fun1():\n",
    "    global num  # 需要使用 global 关键字声明\n",
    "    print('fun1函数里面', num) \n",
    "    num = 123\n",
    "    print('fun1里面，修改值之后',num)\n",
    "fun1()\n",
    "print(u'运行函数后',num)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 错误示范\n",
    "a = 10\n",
    "\n",
    "def test():\n",
    "    a = a + 1\n",
    "    print(a)\n",
    "test() # UnboundLocalError: local variable 'a' referenced before assignment， 程序报错，a变量没有定义"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 推荐做法，传入参数\n",
    "a = 10\n",
    "\n",
    "def test(var_a):\n",
    "    var_a = var_a + 1\n",
    "    print(var_a)\n",
    "\n",
    "test(a)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 不推荐做法  原因：和外面的混在一起\n",
    "a = 10\n",
    "def test():\n",
    "    global a\n",
    "    a = a + 1\n",
    "    print(a)\n",
    "test()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 练习"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 请定义一个 square_of_sum 函数，它接受一个list，返回list中每个元素平方的和\n",
    "\n",
    "def square_of_sum(my_list):\n",
    "    total = 0 # 总和\n",
    "    \n",
    "    for item in my_list:\n",
    "        print(\"{}的平方是{}\".format(item, item**2)) #字符串格式化输出\n",
    "        ???\n",
    "        #print(total)\n",
    "        \n",
    "  # 返回结果\n",
    "    ???\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(square_of_sum([1, 2, 3, 4, 5]))\n",
    "print(square_of_sum([-5, 0, 5, 15, 25]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sum([1,2])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "l = [7,1,2,3]\n",
    "l.sort()\n",
    "l"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 请定义一个 greet() 函数，它包含一个默认参数，\n",
    "# 如果没有传入，打印 'Hello, world.'，如果传入，打印 'Hello, xxx.'\n",
    "\n",
    "def greet(input_string='world'):\n",
    "    print(???)\n",
    "\n",
    "greet()\n",
    "greet(???)\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "传参数顺序：首先定义一般参数，然后定义缺省值参数，最后定义不定长参数"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 扩展知识- 函数式编程\n",
    "\n",
    "#### 传入函数\n",
    "既然变量可以指向函数，函数的参数能接收变量，那么一个函数就可以接收另一个函数作为参数，这种函数就称之为高阶函数。\n",
    "\n",
    "一个最简单的高阶函数：\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, y, f):\n",
    "    return f(x) + f(y)\n",
    "\n",
    "print(add(-5, 6, abs)) # abs本身是一个函数，就是求绝对值的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "abs(-3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Map 函数\n",
    "\n",
    "map()函数接收两个参数，一个是函数，一个是列表，map将传入的函数依次作用到序列的每个元素，并把结果作为新的列表返回\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def f(x):\n",
    "    return x * x\n",
    "\n",
    "r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "print('python2 和 python3 有些不同，在3里面，r是一个迭代器', r)\n",
    "# 这是错误的，print(r[0])，迭代器没有序号，不是队列，每次使用next函数只会返回一个值\n",
    "# print(next(r)) 可以获取下一个值\n",
    "# list(r) # 这样可以把迭代器转换成队列\n",
    "\n",
    "# for循环可以自动判断迭代器的结束，可以用for来遍历\n",
    "for i in r:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 同等效果\n",
    "def f(x):\n",
    "    return x * x\n",
    "\n",
    "L = []\n",
    "for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:\n",
    "    L.append(f(n))\n",
    "print(L)\n",
    "\n",
    "for i in L:\n",
    "    print(i)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### filter\n",
    "和map()类似，filter()也接收一个函数和一个序列。和map()不同的是，filter()把传入的函数依次作用于每个元素，然后根据返回值是True还是False决定保留还是丢弃该元素"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def is_odd(n):\n",
    "    return n % 2 == 1\n",
    "\n",
    "list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Reduce "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "reduce把一个函数作用在一个序列[x1, x2, x3, ...]上，这个函数必须接收两个参数，reduce把结果继续和序列的下一个元素做求和计算，其效果就是："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from functools import reduce  # python2 不需要\n",
    "\n",
    "def fn(x, y):\n",
    "    return x + y   #x代表上一次运算结果，y代表下一个数\n",
    "\n",
    "print(reduce(fn, [1, 3, 5, 7, 9]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 匿名函数\n",
    "\n",
    "当我们在传入函数时，有些时候，不需要显式地定义函数，直接传入匿名函数更方便。\n",
    "\n",
    "关键字lambda表示匿名函数，冒号前面的x表示函数参数。\n",
    "\n",
    "匿名函数有个限制，就是只能有一个表达式，不用写return，返回值就是该表达式的结果。\n",
    "\n",
    "用匿名函数有个好处，因为函数没有名字，不必担心函数名冲突。此外，匿名函数也是一个函数对象，也可以把匿名函数赋值给一个变量，再利用变量来调用该函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 例子\n",
    "list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 相同功能\n",
    "def f(x):\n",
    "    return x * x\n",
    "\n",
    "list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
